#include "LuaWorker.h"
#include <functional>

LuaWorker::LuaWorker(LuaWorkerManager* manager,
	const std::string& name)
	:m_manager(manager), m_name(name), 
	m_lua(nullptr), m_state(State::Init)
{
}


LuaWorker::~LuaWorker() {
	stop();
}

bool LuaWorker::start() {
	if (m_state == State::Running)
		return false;

	std::thread newThread(
		std::bind(&LuaWorker::workerRun, this));
	m_thread = std::move(newThread);
	m_state = State::Running;
	return true;
}

void LuaWorker::stop() {
	if (m_state == State::Running) {
		m_state = State::Stopped;
		m_cv.notify_one();
		if (m_thread.joinable()) {
			m_thread.join();
		}
	}
}


bool LuaWorker::run(TaskPtr task) {
	if (m_state == State::Stopped) {
		return false;
	}

	{
		std::lock_guard < std::mutex > guard(m_taskLock);
		m_tasks.push_back(std::move(task));
	}

	m_cv.notify_one();
	return true;
}

void LuaWorker::workerRun() {
	initLua();
	while (m_state == State::Running) {
		auto task = popTask();
		while (task) {
			m_lua->setGlobal("arg", m_lua->newString(task->param.c_str(), task->param.length()));
			LuaObject lrtn = m_lua->doBuffer(task->script.data(), task->script.length(),"");
			size_t len;
			const char* str = lrtn.toString(&len);
			if (str)
				task->param.assign(str, len);
			else
				task->param.clear();
			finishTask(task);
			task = popTask();
		}

		std::unique_lock<std::mutex> lock(m_cvLock);
		m_cv.wait_for(lock, std::chrono::milliseconds(1000));
	}
	releaseLua();
}

void LuaWorker::finishTask(TaskPtr task) {
	auto observer = LuaWorkerManager::instance()->observer();
	if (observer)
		observer->runFinish(task);
}


LuaWorker::TaskPtr LuaWorker::popTask() {
	std::lock_guard < std::mutex > guard(m_taskLock);
	if (m_tasks.empty()) {
		return nullptr;
	}
	auto task = m_tasks.front();
	m_tasks.pop_front();
	return task;
}

void LuaWorker::initLua() {
	auto lua = new LuaOwnerState();
	auto observer = LuaWorkerManager::instance()->observer();
	if (observer)
		observer->initLua(lua);
	m_lua = lua;
}

void LuaWorker::releaseLua() {
	delete m_lua;
	m_lua = nullptr;
}

LuaWorkerManager* LuaWorkerManager::s_manager = nullptr;


LuaWorkerManager::LuaWorkerManager() 
	:m_observer(nullptr)
{
}

LuaWorkerManager::~LuaWorkerManager() {

}

LuaWorkerManager* LuaWorkerManager::instance() {
	if (!s_manager)
		s_manager = new LuaWorkerManager();
	return s_manager;
}


void LuaWorkerManager::registerObserver(LuaWorkerObserver* obs) {
	m_observer = obs;
}


LuaWorker* LuaWorkerManager::createWorker(const std::string& name) {
	std::lock_guard<std::mutex> guard(m_lock);
	auto find = m_workers.find(name);
	if (find != m_workers.end()) {
		return find->second;
	}
	auto worker = new LuaWorker(this, name);
	worker->start();
	m_workers[worker->name()] = worker;
	return worker;
}

void LuaWorkerManager::destroyWorker(const std::string& name) {
	LuaWorker * worker = nullptr;
	{
		std::lock_guard<std::mutex> guard(m_lock);
		auto find = m_workers.find(name);
		if (find != m_workers.end()) {
			worker = find->second;
			m_workers.erase(find);
		}
	}
	delete worker;
}

bool LuaWorkerManager::runOnWorker(const std::string& name, LuaWorker::TaskPtr task) {
	std::lock_guard<std::mutex> guard(m_lock);
	auto find = m_workers.find(name);
	if (find != m_workers.end()) {
		return find->second->run(task);
	} else {
		return false;
	}
}
